var dgram = require('dgram');
var http = require('http');
var util = require('util');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

/**
 * 
 * Data model for device records
 * 
 * @class Device
 * @author jmanero
 * 
 * @requires dgram
 * @requires http
 * @requires util
 * @requires mongoose
 * 
 * @constructor
 * 
 */
var Device = new Schema({
	/**
	 * MAC Address of device Ethernet/Wi-Fi interface
	 * 
	 * @memberOf Device
	 */
	hwAddress : /** String */
	{
		type : String,
		unique : true
	},
	/**
	 * IP Address of device
	 * 
	 * @memberOf Device
	 */
	inetAddress : /** String */
	{
		type : String
	},
	/**
	 * Common name of device
	 * 
	 * @memberOf Device
	 */
	name : /** String */
	{
		type : String,
		unique : true
	},
	/**
	 * Operational status of device
	 * 
	 * @memberOf Device
	 */
	status : /** String */
	{
		type : String,
		index : true,
		enum : [ 'online', 'offline', 'noconf' ]
	},
	/**
	 * META Information: Location of device
	 * 
	 * @memberOf Device
	 */
	location : /** String */
	{
		type : String
	},
	/**
	 * META Information: Extra user-defined information
	 * 
	 * @memberOf Device
	 */
	description : /** String */
	{
		type : String
	}
});

/**
 * Fire off any one-time tasks after Device is defined
 * 
 * @memberOf Device
 * @static
 * 
 */
Device.statics.startup = function() {
// model.Message.log('Info', 'DEVICE STARTUP!!');
	/**
	 * Create and remove a dummy device to ensure indexes are properly populated
	 * 
	 * @memberOf Device
	 */
	var d = new model.Device();
	d.hwAddress = '00-00-00-00-00-00';
	d.inetAddress = '0.0.0.0';
	d.name = 'setup';
	d.status = 'offline';
	d.location = 'nowhere';
	d.description = 'nothing';
	d.save(function(e) {
// model.Message.log('Info', 'SETUP DEVICE SAVE!!');
		model.Message.error(e);
		model.Device.remove({
			hwAddress : '00-00-00-00-00-00'
		}, function(e) {
			model.Message.error(e);
		});
		// model.Device.find({hwAddress : '00-00-00-00-00-00'}, function(e, ds)
		// {
// model.Device.find({}, function(e, ds) {
// model.Message.log('Info', 'AHHHHHHHHH!');
// ds.forEach(function(d) {
// model.Message.log('Info', 'Found Device : ' + d.name + '('
// + d.hwAddress + ')');
// });
		});

	/**
	 * 
	 * UDP Listener for Locator protocol
	 * 
	 * @name listenerSocket
	 * @memberOf Device
	 * @type drgam.Socket
	 * 
	 */
	this.locatorSocket = dgram.createSocket("udp4");
	this.locatorSocket.setBroadcast(true);
	this.locatorSocket.bind(30303, '172.16.1.254');

	/**
	 * Process discovery response
	 * 
	 * @name message
	 * @event
	 * @memberOf Device
	 */
	this.locatorSocket.on('message', function(message, rinfo) {
		var messageFields = message.toString().match(
				/(.*?)\s+\r\n([0-9a-fA-f\-]{17})/);

		var hwAddress = messageFields[2].toLowerCase();
		var name = messageFields[1].toLowerCase();

		model.Device.findOne({
			hwAddress : hwAddress
		}, function(e, device) {
			if (device) {
				device.status = 'online';
				device.inetAddress = rinfo.address;
				device.save(function(e) {
					model.Message.error(e);
				});
			} else {
				var n = new model.Device();
				n.hwAddress = hwAddress;
				n.inetAddress = rinfo.address;
				n.name = name;
				n.status = 'noconf';
				n.location = '';
				n.description = '';
				n.save(function(e) {
					model.Message.error(e);
				});
			}
		});
	});
};

/**
 * 
 * Implements the Microchip locator protocol. Generates a UDP broadcast and
 * listens for responses from hardware devices.
 * 
 * @memberOf Device
 * @static
 * 
 */
Device.statics.locate = function() {
	var msg = new Buffer("Discovery: Who is out there?");
	this.locatorSocket.send(msg, 0, msg.length, 30303, '255.255.255.255',
			function(e) {
				model.Message.error(e);

				model.Device.remove({
					status : 'noconf'
				}, function(e) {
					model.Message.error(e);
				});

				model.Device.update({
					status : 'online'
				}, {
					status : 'offline'
				}, function(e) {
					model.Message.error(e);
				});
			});
};

Device.statics.sync = function() {
	this.find({
		status : 'online'
	}, function(e, devices) {
		model.Message.error(e);
		devices.forEach(function(device) {
			device.sync();
		});
	});
};

/**
 * 
 * Data transfer
 * 
 * @memberOf Device
 * 
 */
Device.methods.sync = function() {
	var device = this;
	var resData = '';
	var request = http.request({
		host : device.inetAddress,
		port : 9001,
		method : 'POST',
		path : '/sync.cgi'
	}, function(response) {
		response.setEncoding('utf8');
		response.on('data', function(chunk) {
			resData += chunk;
		});
		response.on('end', function() {
			var ports = JSON.parse(resData);
			for ( var p in ports) {
				model.Port.update({
					device : device._id,
					name : p
				}, {
					value : ports[p]
				}, function(e) {
					model.Message.error(e);
				});
			}
		});
	});

	model.Port.find({
		device : device._id,
		direction : 'Output'
	}, function(e, ports) {
		model.Message.error(e);
		ports.forEach(function(port) {
			request.write(port.name + '=' + port.value + '&');
			model.Message.log('Info', 'Write to ' + device.name + ': '
					+ port.name + '=' + port.value + '&');
		});
		// util.log('End Request to ' + device.name);
		request.end();
	});

};

model.Device = connection.model('Device', Device);
